﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.Threading;
using Odyssey.Utils;

namespace Odyssey.Effects
{
    /// <summary>
    /// Core functions for PageEffects which are used for both PageEffectBehavior and PageEffectAction classes.
    /// </summary>
    public abstract class PageEffect
    {
        private bool isActive;
        private bool isInitialized = true;

        public PageEffect()
            : base()
        {
            Initialize();
        }

        /// <summary>
        /// Gets the duration in seconds to perform the current transition. 
        /// This property is automatically calculated after a Storyboard is created and reflects to total duration of the storyboard.
        /// </summary>
        public double DurationInSeconds { get; private set; }

        /// <summary>
        /// Gets whether the effect has been set previously.
        /// </summary>
        public bool IsInitialized { get { return isInitialized; } internal set { isInitialized = value; } }

        /// <summary>
        /// Gets whether the effect is active.
        /// </summary>
        public bool IsActive
        {
            get { return isActive; }
            set
            {
                if (!isInitialized || isActive != value)
                {
                    isActive = value;
                    ChangeActivation(value);
                    IsInitialized = true;
                }
            }
        }

        /// <summary>
        /// Gets the storyboard that performs the transition.
        /// </summary>
        protected Storyboard Storyboard { get; private set; }

        /// <summary>
        /// Gets or sets the duration for the transition.
        /// </summary>
        public TimeSpan Duration
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the page for which the effect applies. This value is set either by a PageEffectBehavior or a PageEffectAction class.
        /// </summary>
        internal PhoneApplicationPage Page { get; set; }

        /// <summary>
        /// Initializes the default property values.
        /// </summary>
        protected virtual void Initialize()
        {
            Duration = TimeSpan.FromMilliseconds(400.0);
        }

        /// <summary>
        /// Prepares the specified storyboard to add Timelines for the appropriate animation for activating the effect.
        /// </summary>
        /// <param name="page">Page for which the transition will be applied.</param>
        /// <param name="storyboard">Storyboard to add Timelines.</param>
        protected abstract void Activate(PhoneApplicationPage page, Storyboard storyboard);

        /// <summary>
        /// Prepares the specified storyboard to add Timelines for the appropriate animation for deactivating the effect.
        /// </summary>
        /// <param name="page">Page for which the transition will be applied.</param>
        /// <param name="storyboard">Storyboard to add Timelines.</param>
        /// <param name="isActivated">
        /// True to indicate that the effect was previously set, otherwise false. 
        /// If this value is false, Timelines usually need the From property set to a value which would be available after the effect is activated.
        /// </param>
        protected abstract void Deactivate(PhoneApplicationPage page, Storyboard storyboard, bool isActivated);

        /// <summary>
        /// Pauses the animation between activation and deactivation or vice versa.
        /// </summary>
        public virtual void Stop()
        {
            if (Storyboard != null && Storyboard.GetCurrentState() == ClockState.Active) Storyboard.Pause();
            Storyboard = null;
        }


        protected void AddTimeline(Timeline timeline, DependencyObject target, string propertyPath)
        {
            timeline.FillBehavior = FillBehavior.HoldEnd;
            timeline.Duration = this.Duration;
            Storyboard.SetTarget(timeline, target);
            Storyboard.SetTargetProperty(timeline, new PropertyPath(propertyPath));
            Storyboard.Children.Add(timeline);
        }

        protected void AddTimeline(Timeline timeline, DependencyObject target, string propertyPath, TimeSpan duration)
        {
            timeline.FillBehavior = FillBehavior.HoldEnd;
            timeline.Duration = duration;
            Storyboard.SetTarget(timeline, target);
            Storyboard.SetTargetProperty(timeline, new PropertyPath(propertyPath));
            Storyboard.Children.Add(timeline);
        }


        private void ChangeActivation(bool isActive)
        {
            if (Page == null) throw new ArgumentException("Page");
            Stop();
            Storyboard = new Storyboard();
            Storyboard.FillBehavior = FillBehavior.HoldEnd;
            if (isActive) Activate(Page, Storyboard); else Deactivate(Page, Storyboard, IsInitialized);
            Storyboard.SetTarget(Storyboard, Page);

            DurationInSeconds = GetDuratonInSeconds(Storyboard);

            AddActivationEvents(isActive, Storyboard);
            if (IsInitialized && isActive)
            {
                Storyboard.Begin();
            }
            else
            {
                ////VisualTreeExtensions.ScheduleOnNextRender(() =>
                ////    {
                ////        VisualTreeExtensions.ScheduleOnNextRender(Storyboard.Begin);
                ////    });
                Page.Dispatcher.BeginInvoke(() =>
                    {
                        Page.UpdateLayout();
                        Page.Dispatcher.BeginInvoke(() => Storyboard.Begin());
                    });
            }
        }


        void HandlePageUnloaded(object sender, RoutedEventArgs e)
        {
            PhoneApplicationPage page = sender as PhoneApplicationPage;
            page.Unloaded -= HandlePageUnloaded;

            if (Storyboard != null && Storyboard.GetCurrentState() == ClockState.Active)
            {
                Storyboard.SkipToFill();
            }
        }

        private void AddActivationEvents(bool isActive, Storyboard sb)
        {
            // Page.Unloaded += new RoutedEventHandler(HandlePageUnloaded);
            sb.Completed += new EventHandler(OnStoryboardCompleted);



        }

        void OnStoryboardCompleted(object sender, EventArgs e)
        {
            if (!isActive) OnActivated();
            else OnDeactivated();
        }

        protected double GetDuratonInSeconds(System.Windows.Media.Animation.Storyboard storyboard)
        {
            double duration = 0;

            foreach (Timeline timeline in storyboard.Children)
            {
                double d = timeline.Duration.TimeSpan.TotalSeconds;
                var beginTime = timeline.BeginTime;
                if (beginTime.HasValue) d += beginTime.Value.TotalSeconds;
                duration = Math.Max(duration, d);
            }
            return duration;
        }

        protected void OnActivated()
        {
            var eh = Activated;
            if (eh != null) eh(this, EventArgs.Empty);
        }

        protected void OnDeactivated()
        {
            var eh = Deactivated;
            if (eh != null) eh(this, EventArgs.Empty);
        }


        public event EventHandler Activated;
        public event EventHandler Deactivated;

    }
}
